package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.ObjectUtil;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.zzyl.base.PageResponse;
import com.zzyl.base.ResponseResult;
import com.zzyl.constant.AccraditationRecordConstant;
import com.zzyl.constant.RetreatConstant;
import com.zzyl.dto.CheckInDto;
import com.zzyl.dto.ElderDto;
import com.zzyl.dto.MemberElderDto;
import com.zzyl.entity.*;
import com.zzyl.mapper.*;
import com.zzyl.service.*;
import com.zzyl.utils.CodeUtil;
import com.zzyl.utils.UserThreadLocal;
import com.zzyl.vo.*;
import com.zzyl.vo.retreat.ElderVo;
import com.zzyl.vo.retreat.TasVo;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Service
public class CheckInServiceImpl implements CheckInService {

    @Autowired
    private ElderService elderService;

    @Autowired
    private CheckInMapper checkInMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private AccraditationRecordService accraditationRecordService;

    @Autowired
    private ActFlowCommService actFlowCommService;


    @Override
    public ResponseResult createCheckIn(CheckInDto dto) {
//        1,根据老人的姓名和身份证号码去老人表中查询信息
//        2,如果能查到老人信息,并且是申请中的状态,则直接结束该方法
        ElderVo elderVo = elderService.selectByIdCardAndName(dto.getElderDto().getIdCardNo(), dto.getElderDto().getName());
        if (ObjectUtil.isNotEmpty(elderVo) && elderVo.getStatus().equals(4)) {
            return ResponseResult.error(dto.getElderDto().getName() + "已经发起过入住申请!");
        }
//        3,如果查不到,保存老人信息到老人表
        ElderDto elderDto = dto.getElderDto();
        //入参中的ElderDto老人信息不全,所以需要补全
        elderDto.setStatus(4);//入住中
        JSONObject jsonObject = JSONUtil.toBean(dto.getOtherApplyInfo(), JSONObject.class);
        elderDto.setAge(jsonObject.get("age").toString());
        elderDto.setSex(jsonObject.get("sex").toString());
        elderDto.setImage(dto.getUrl1());
        Elder elder = elderService.insert(elderDto);
        dto.setElderDto(elderDto);


//        4,保存或者修改老人信息到入住表
        CheckIn checkIn = BeanUtil.copyProperties(dto, CheckIn.class);
        String subject = UserThreadLocal.getSubject();
        User user = JSONUtil.toBean(subject, User.class);

        if (ObjectUtil.isNotEmpty(dto.getId())) {
            //修改入住信息操作
            checkIn.setFlowStatus(CheckIn.FlowStatus.REVIEW.getCode());
            checkIn.setElderId(elder.getId());
            checkIn.setTitle(elder.getName() + "的入住申请");
            checkIn.setOtherApplyInfo(JSONUtil.toJsonStr(dto));

            checkInMapper.updateByPrimaryKeySelective(checkIn);
        } else {
            //新增入住信息操作
            // TODO 这一块是把前端传入的整个dto参数全部封装到了OtherApplyInfo这个字段中,
            //  就是为了将来表单查询的时候,方便取出
            checkIn.setOtherApplyInfo(JSONUtil.toJsonStr(dto));
            //补全checkIn剩余属性
            String code = CodeUtil.generateCode("RZ", redisTemplate, 5);
            checkIn.setCheckInCode(code);//编码
            checkIn.setTitle(elder.getName() + "的入住申请");//标题
            checkIn.setElderId(elder.getId());//老人id

            checkIn.setCounselor(user.getRealName());//养老顾问名称
            checkIn.setApplicat(user.getRealName());//申请人
            checkIn.setDeptNo(user.getDeptNo());//部门编号
            checkIn.setApplicatId(user.getId());//申请人id
            checkIn.setFlowStatus(CheckIn.FlowStatus.REVIEW.getCode());//流程状态  入住评估
            checkIn.setStatus(CheckIn.Status.APPLICATION.getCode());//审核状态  申请中
            checkInMapper.insert(checkIn);
        }

        //判断当前是否存在任务ID,如果存在,则直接完成该任务
        if (ObjectUtil.isNotEmpty(dto.getTaskId())) {
            //完成任务
            actFlowCommService.completeProcess(
                    checkIn.getTitle(),
                    dto.getTaskId(),
                    user.getId().toString(),
                    1,
                    checkIn.getStatus()
            );
        } else {
            //如果不存在,才需要设置流程变量和启动流程实例
            //        5,准备设置流程变量,业务key
            Map<String, Object> map = setVariables(checkIn);
//        6,启动流程实例
            actFlowCommService.start(checkIn.getId(), "checkIn", user, map, true);
        }


//        9,保存操作记录
        //9.1获取下一个审核人id
        Long nextAssigneeId = actFlowCommService.getNextAssignee("checkIn", "checkIn:" + checkIn.getId());

        RecordVo recordVo = getRecordVo(
                checkIn,
                user,
                AccraditationRecordConstant.AUDIT_STATUS_PASS,
                "同意",
                "发起申请-申请入住",
                "护理组组长处理-入住评估",
                nextAssigneeId,
                AccraditationRecordConstant.RECORD_HANDLE_TYPE_PROCESSED
        );

        accraditationRecordService.insert(recordVo);
        return ResponseResult.success();
    }

    @Autowired
    private AccraditationRecordMapper accraditationRecordMapper;

    @Autowired
    private CheckInConfigService checkInConfigService;

    @Autowired
    private NursingLevelService nursingLevelService;

    @Autowired
    private ContractMapper contractMapper;

    /**
     * 查询入住表单
     *
     * @param assigneeId 处理人ID
     * @param code       入住编码
     * @param flowStatus 流程状态
     * @param taskId     任务ID
     * @return
     */
    @Override
    public ResponseResult<TasVo> getCheckIn(String assigneeId, String code, Integer flowStatus, String taskId) {
        //1,根据入住编码查询入住信息
        CheckIn checkIn = checkInMapper.selectByCheckInCode(code);
        CheckInVo checkInVo = BeanUtil.toBean(checkIn, CheckInVo.class);

        Contract contract = contractMapper.selectByElderId(checkIn.getElderId());
        ContractVo contractVo = BeanUtil.toBean(contract, ContractVo.class);
        checkInVo.setContractVo(contractVo);

        CheckInConfig checkInConfig = checkInConfigService.findCurrentConfigByElderId(checkIn.getElderId());
        CheckInConfigVo checkInConfigVo = BeanUtil.toBean(checkInConfig, CheckInConfigVo.class);

        if(ObjectUtil.isNotEmpty(checkInConfig) && ObjectUtil.isNotEmpty(checkInConfig.getRemark())){
            //1:7:10:1楼:107
            String remark = checkInConfig.getRemark();
            String[] split = remark.split(":");
            checkInConfigVo.setFloorId(Long.valueOf(split[0]));
            checkInConfigVo.setRoomId(Long.valueOf(split[1]));
            checkInConfigVo.setBedId(Long.valueOf(split[2]));
            checkInConfigVo.setFloorName(split[3]);
            checkInConfigVo.setCode(split[4]);

            //设置护理级别vo对象
            NursingLevelVo nursingLevelVo = nursingLevelService.getById(checkInConfig.getNursingLevelId());
            checkInVo.setNursingLevelVo(nursingLevelVo);
        }



        checkInVo.setCheckInConfigVo(checkInConfigVo);




        //设置护理主管的评估信息
        if (ObjectUtil.isNotEmpty(checkIn.getReviewInfo())) {
            JSONObject jsonObject = JSON.parseObject(checkIn.getReviewInfo());
            String reviewInfo = jsonObject.getString("reviewInfo");
            String reviewInfo1 = jsonObject.getString("reviewInfo1");
            String reviewInfo2 = jsonObject.getString("reviewInfo2");
            checkInVo.setReviewInfo(reviewInfo);
            checkInVo.setReviewInfo1(reviewInfo1);
            checkInVo.setReviewInfo2(reviewInfo2);
        }


        String otherApplyInfoAll = checkIn.getOtherApplyInfo();
        JSONObject jsonObjectAll = JSON.parseObject(otherApplyInfoAll, JSONObject.class);
        if (ObjectUtil.isNotEmpty(jsonObjectAll)) {
            //获取老人的其他信息
            String otherApplyInfo = jsonObjectAll.getString("otherApplyInfo");
            checkInVo.setOtherApplyInfo(otherApplyInfo);

            //获取老人家属信息
            String memberElderDtosJsonStr = jsonObjectAll.getString("memberElderDtos");
            List<MemberElderDto> memberElderDtoList = JSON.parseArray(memberElderDtosJsonStr, MemberElderDto.class);
            checkInVo.setMemberElderDtos(memberElderDtoList);

            //获取老人信息
            String elderDtoJsonStr = jsonObjectAll.getString("elderDto");
            ElderDto elderDto = JSON.parseObject(elderDtoJsonStr, ElderDto.class);
            checkInVo.setElderDto(elderDto);

            //老人的照片和身份证照片
            checkInVo.setUrl1(jsonObjectAll.getString("url1"));
            checkInVo.setUrl2(jsonObjectAll.getString("url2"));
            checkInVo.setUrl3(jsonObjectAll.getString("url3"));
        }
        /*
        isShow: 0:只展示操作记录  1:展示详细数据和操作记录
         */
        Integer isShow = 1;
        //前端传过来的flowStatus默认为-1,因为他也不知道当前走到了哪一步,所以需要我们做一个判断
        //判断当前登录人是否是该任务的执行人,如果是,则返回1
        //如果不是执行人,则返回0
        // flowStatus 0 1 2 3 4    -1
        if (flowStatus < 0) {
            flowStatus = checkIn.getFlowStatus();
        }
        if (ObjectUtil.isNotEmpty(taskId)) {
            /*
            taskId: 任务id
            flowStatus: 用户点击的流程节点
            dbFlowStatus: 目前数据库里真实的流程状态
             */
            isShow = actFlowCommService.isCurrentUserAndStep(taskId, flowStatus, checkIn.getFlowStatus());
        }

        Boolean isRevocation = false;
        if (isShow == 2 && checkIn.getStatus().equals(CheckIn.Status.APPLICATION.getCode())) {
            isRevocation = true;
        }
        if (isShow == 2) {
            isShow = 1;
        }

        TasVo tasVo = new TasVo();
        tasVo.setCheckIn(checkInVo);
        tasVo.setType(AccraditationRecordConstant.RECORD_TYPE_CHECK_IN);//入住
        tasVo.setIsShow(isShow);

        //判断是否显示撤回按钮
        tasVo.setIsRevocation(isRevocation);

        //查询操作记录
        List<AccraditationRecord> accraditationRecordList = accraditationRecordMapper.getAccraditationRecordByBuisId(checkIn.getId(), AccraditationRecordConstant.RECORD_TYPE_CHECK_IN);
        tasVo.setAccraditationRecords(accraditationRecordList);

        return ResponseResult.success(tasVo);
    }

    @Autowired
    private TaskService taskService;

    /**
     * 护理主管评估
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult review(CheckInDto dto) {
        //设置流程状态更新入住表数据
        CheckIn checkIn = checkInMapper.selectByPrimaryKey(dto.getId());
        checkIn.setFlowStatus(CheckIn.FlowStatus.APPROVAL.getCode());
        checkIn.setReviewInfo(JSON.toJSONString(dto));
        checkInMapper.updateByPrimaryKeySelective(checkIn);
        //完成任务
        actFlowCommService.completeProcess(null, dto.getTaskId(), null, 1, null);

        //保存操作记录
        //获取下一个审核人id
        Long nextAssignee = actFlowCommService.getNextAssignee("checkIn", "checkIn:" + checkIn.getId());

        String subject = UserThreadLocal.getSubject();
        User user = JSON.parseObject(subject, User.class);
        RecordVo recordVo = getRecordVo(
                checkIn,
                user,
                AccraditationRecordConstant.AUDIT_STATUS_PASS,
                "同意",
                "护理组组长处理-入住评估",
                "院长处理-入住审批",
                nextAssignee,
                AccraditationRecordConstant.RECORD_HANDLE_TYPE_PROCESSED
        );
        accraditationRecordService.insert(recordVo);
        return ResponseResult.success();
    }

    /**
     * 副院长审批-通过
     *
     * @param id      入住id
     * @param message 意见
     * @param taskId  任务id
     * @return
     */
    @Override
    public ResponseResult submit(Long id, String message, String taskId) {
        //查询入住信息,更新流程状态
        CheckIn checkIn = checkInMapper.selectByPrimaryKey(id);
        checkIn.setFlowStatus(CheckIn.FlowStatus.CONFIG.getCode());
        checkInMapper.updateByPrimaryKeySelective(checkIn);

        //完成任务
        actFlowCommService.completeProcess(null, taskId, null, 1, null);

        String subject = UserThreadLocal.getSubject();
        User user = JSON.parseObject(subject, User.class);
        Long nextAssignee = actFlowCommService.getNextAssignee("checkIn", "checkIn:" + checkIn.getId());
        //保存操作记录
        RecordVo recordVo = getRecordVo(
                checkIn,
                user,
                AccraditationRecordConstant.AUDIT_STATUS_PASS,
                message,
                "院长处理-入住审批",
                "养老顾问处理-入住配置",
                nextAssignee,
                AccraditationRecordConstant.RECORD_HANDLE_TYPE_AUDIT
        );
        accraditationRecordService.insert(recordVo);
        return ResponseResult.success();
    }


    /**
     * 副院长审批-拒绝
     *
     * @param id      入住ID
     * @param message 意见
     * @param taskId  任务ID
     * @return
     */
    @Override
    public ResponseResult reject(Long id, String message, String taskId) {
        //查询入住信息,更新流程状态
        CheckIn checkIn = checkInMapper.selectByPrimaryKey(id);
        checkIn.setFlowStatus(CheckIn.FlowStatus.CONFIG.getCode());
        checkIn.setStatus(CheckIn.Status.CLOSED.getCode());
        checkInMapper.updateByPrimaryKey(checkIn);

        //删除老人数据
        elderService.deleteByPrimaryKey(checkIn.getElderId());
        String subject = UserThreadLocal.getSubject();
        User user = JSON.parseObject(subject, User.class);

        //完成任务
        actFlowCommService.completeProcess(null, taskId, user.getId().toString(), 2, CheckIn.Status.CLOSED.getCode());

        //保存操作记录
        RecordVo recordVo = getRecordVo(
                checkIn,
                user,
                AccraditationRecordConstant.AUDIT_STATUS_REJECT,
                message,
                "院长处理-入住审批",
                "",
                null,
                AccraditationRecordConstant.RECORD_HANDLE_TYPE_AUDIT
        );
        accraditationRecordService.insert(recordVo);

        return ResponseResult.success();
    }

    /**
     * 副院长审批-驳回
     *
     * @param id      入住ID
     * @param message 意见
     * @param taskId  任务ID
     * @return
     */
    @Override
    public ResponseResult disapprove(Long id, String message, String taskId) {
        //查询入住信息,更新流程状态
        CheckIn checkIn = checkInMapper.selectByPrimaryKey(id);
        checkIn.setFlowStatus(CheckIn.FlowStatus.APPLY.getCode());
        //清除评估数据
        //checkIn.setReviewInfo(null);
        checkInMapper.updateByPrimaryKeySelective(checkIn);

        //删除老人数据elder
        elderService.deleteByPrimaryKey(checkIn.getElderId());

        //完成任务
        actFlowCommService.completeProcess(
                null,
                taskId,
                null,
                AccraditationRecordConstant.AUDIT_STATUS_DISAPPROVE,
                null
        );

        String subject = UserThreadLocal.getSubject();
        User user = JSON.parseObject(subject, User.class);

        //保存操作记录
        RecordVo recordVo = getRecordVo(
                checkIn,
                user,
                AccraditationRecordConstant.AUDIT_STATUS_DISAPPROVE,
                message,
                "驳回申请-入住审批",
                "养老顾问处理-入住申请",
                checkIn.getApplicatId(),
                AccraditationRecordConstant.RECORD_HANDLE_TYPE_PROCESSED
        );
        accraditationRecordService.insert(recordVo);


        return ResponseResult.success();
    }

    /**
     * 副院长-撤回
     *
     * @param flowStatus 流程状态
     * @param id         入住ID
     * @param taskId     任务ID
     * @return
     */
    @Override
    public ResponseResult revocation(Integer flowStatus, Long id, String taskId) {
        //1,更新流程状态
        CheckIn checkIn = checkInMapper.selectByPrimaryKey(id);
        checkIn.setFlowStatus(checkIn.getFlowStatus() - 1);
        checkInMapper.updateByPrimaryKeySelective(checkIn);

        String subject = UserThreadLocal.getSubject();
        User user = JSON.parseObject(subject, User.class);

        String name = EnumUtil.getBy(CheckIn.FlowStatus::getCode, checkIn.getFlowStatus()).getName();

        //保存操作记录
        RecordVo recordVo = getRecordVo(
                checkIn,
                user,
                AccraditationRecordConstant.AUDIT_STATUS_WITHDRAWS,
                "撤回",
                "撤回申请-" + name,
                "",
                user.getId(),
                AccraditationRecordConstant.RECORD_HANDLE_TYPE_PROCESSED
                );
        accraditationRecordService.insert(recordVo);

        //撤回
        actFlowCommService.withdrawTask(taskId,false);

        return ResponseResult.success();
    }
    /**
     * 申请人-撤销
     * @param id 入住ID
     * @param taskId 任务ID
     * @return
     */
    @Override
    public ResponseResult cancel(Long id, String taskId) {
        //更新入住表的审核状态为已关闭
        CheckIn checkIn = checkInMapper.selectByPrimaryKey(id);
        checkIn.setStatus(CheckIn.Status.CLOSED.getCode());
        checkInMapper.updateByPrimaryKeySelective(checkIn);
        //删除老人表的信息
        elderService.deleteByPrimaryKey(checkIn.getElderId());
        //保存操作记录
        String subject = UserThreadLocal.getSubject();
        User user = JSON.parseObject(subject, User.class);
        RecordVo recordVo = getRecordVo(
                checkIn,
                user,
                AccraditationRecordConstant.AUDIT_STATUS_CANCEL,
                "撤销申请",
                "撤销申请-入住申请",
                "",
                null,
                AccraditationRecordConstant.RECORD_HANDLE_TYPE_PROCESSED
        );
        accraditationRecordService.insert(recordVo);
        //撤销操作  删除流程实例
        actFlowCommService.closeProcess(taskId,CheckIn.Status.CLOSED.getCode());
        return ResponseResult.success();
    }

    @Override
    public ResponseResult<PageResponse<CheckInVo>> selectByPage(String checkInCode, String name, String idCardNo, LocalDateTime start, LocalDateTime end, Integer pageNum, Integer pageSize, String deptNo, Long userId) {
        PageHelper.startPage(pageNum, pageSize);
        Page<List<CheckIn>> lists = checkInMapper.selectByPage(checkInCode, name, idCardNo, start, end, userId, deptNo);
        return ResponseResult.success(PageResponse.of(lists, CheckInVo.class));
    }

    /**
     * 获取操作记录RecordVo
     *
     * @param checkIn        入住对象
     * @param user           当前用户
     * @param status         审批状态 1:通过 2:拒绝 3:驳回  4:撤回  5:撤销
     * @param option         意见
     * @param step           当前步骤
     * @param nextStep       下一个操作步骤
     * @param nextAssigneeId 下一个审核人id
     * @param handleType     处理类型 0:已审批 1：已处理
     * @return
     */
    private RecordVo getRecordVo(CheckIn checkIn, User user, Integer status, String option, String step, String nextStep, Long nextAssigneeId, Integer handleType) {
        RecordVo recordVo = new RecordVo();
        recordVo.setId(checkIn.getId());
        recordVo.setType(AccraditationRecordConstant.RECORD_TYPE_CHECK_IN);
        recordVo.setFlowStatus(checkIn.getFlowStatus());
        recordVo.setHandleType(handleType);
        recordVo.setStatus(status);
        recordVo.setOption(option);
        recordVo.setStep(step);
        recordVo.setNextStep(nextStep);
        recordVo.setRealName(user.getRealName());
        recordVo.setUserId(user.getId());
        recordVo.setNextAssignee(nextAssigneeId);
        return recordVo;
    }


    @Autowired
    private DeptMapper deptMapper;

    @Autowired
    private UserMapper userMapper;

    /**
     * 设置流程变量
     *
     * @param checkIn
     * @return
     */
    public Map<String, Object> setVariables(CheckIn checkIn) {
        Map<String, Object> map = new HashMap<>();
        //养老顾问的id
        map.put("assignee0", checkIn.getApplicatId());
        //根据部门编号查询护理部的主管id
        Dept dept = deptMapper.selectByDeptNo(RetreatConstant.NURSING_DEPT_CODE);
        map.put("assignee1", dept.getLeaderId());
        //根据部门编号查询院长的id
        List<Long> list = userMapper.selectByDeptNo(RetreatConstant.DEAN_OFFICE_DEPT_CODE);
        map.put("assignee2", list.get(0));

        map.put("assignee3", checkIn.getApplicatId());
        //根据部门编号查询法务部的员工id
        List<Long> fwList = userMapper.selectByDeptNo(RetreatConstant.LEGAL_DEPT_CODE);
        map.put("assignee4", fwList.get(fwList.size() - 1));

        // TODO 接下来在设置几个参数,给后续查询我的待办任务使用的,暂时不用关注
        /**
         * 设置类型
         * 1:退住
         * 2:请假
         * 3:入住
         */
        map.put("processType", 3);
        //设置入住单号
        map.put("processCode", checkIn.getCheckInCode());
        //设置流程状态
        /**
         * 状态（1：申请中，2:已完成,3:已关闭）
         */
        map.put("processStatus", 1);

        //设置申请人名称
        map.put("assignee0Name", checkIn.getApplicat());

        //设置标题
        map.put("processTitle", checkIn.getTitle());

        //设置申请时间
        map.put("applicationTime", checkIn.getCreateTime());

        //设置入住主键id
        map.put("processCheckInId", checkIn.getId());

        return map;
    }
}
